﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Isuka;
using Isuka.Iteration;
using Isuka.Spiritual;
using Isuka.Spiritual.Appendix;

namespace Isuka.Samples.Spiritual.Calc
{
    class Program
    {
        public static readonly ParameterExpression X = Expression.Parameter(typeof(int), "x");

        class ShowNumber : CharGrammer<ExpressionBuilder>
        {
            public ShowNumber()
            {
                var expr = NewRule();
                var integer = Int32[(a, x) => a.Constant(x)];
                var varx = Eq('x')[a => a.Push(X)];

                expr.Rule = integer | varx;

                Rule = expr;
                Skipper = SkipIf(char.IsWhiteSpace);
            }
        }

        class SimpleCalc
            : CharGrammer<ExpressionBuilder>
        {
            public SimpleCalc()
            {
                var expr = NewRule();
                var term = NewRule();
                var fctr = NewRule();
                var add = NewRule();
                var sub = NewRule();
                var mul = NewRule();
                var div = NewRule();

                var integer = Int32[(a, x) => a.Constant(x)];
                var varx = Eq('x')[a => a.Push(X)];

                expr.Rule = term % (add | sub).ZeroOrMore();

                term.Rule = fctr % (mul | div).ZeroOrMore();

                add.Rule = '+' % term[a => a.Add()];
                sub.Rule = '-' % term[a => a.Subtract()];
                mul.Rule = '*' % fctr[a => a.Multiply()];
                div.Rule = '/' % fctr[a => a.Divide()];

                fctr.Rule = integer
                          | varx
                          | '(' % expr % ')'
                          | Epsilon
                          ;

                Rule = expr;
                Skipper = SkipIf(char.IsWhiteSpace);
            }

        }

        static int xValue = 1;

        static void Run<T>(string src, int ans)
            where T : CharGrammer<ExpressionBuilder>, new()
        {
            var sw = Stopwatch.StartNew();
            var gr = new T();
            sw.Stop();
            Console.WriteLine("{0}({1})-----------------", gr, src);
            Console.WriteLine("Ctor {0}ms", sw.ElapsedMilliseconds);

            var eb = new ExpressionBuilder();

            sw.Restart();
            var result = gr.Run(src.AsIterable(), eb);
            sw.Stop();
            Console.WriteLine("Parse {0}ms", sw.ElapsedMilliseconds);
            Console.WriteLine("Source = {0}", src);

            if (result.Match)
            {
                var expr = Expression.Lambda<Func<int, int>>(eb.Pop(), X);
                Console.WriteLine("Expression = {0}", expr);
                Console.WriteLine("Expression({0}) = {1}", xValue, expr.Compile()(xValue));
                Console.WriteLine("Ans({0})", ans);
            }
            else
            {
                Console.WriteLine("Parse is Failed");
            }
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            Run<ShowNumber>("-12", -12);
            Run<ShowNumber>("x", xValue);
            Run<SimpleCalc>("x + 1", xValue + 1);
            Run<SimpleCalc>("(x + 1) / 2", (xValue + 1) / 2);
            Run<SimpleCalc>("5 - 2 * (x + 1) / -2", 5 - 2 * (xValue + 1) / -2);

            Console.ReadKey();
        }
    }
}
